require "argot"
-- from bookstore.BookNotFoundException import BookNotFoundException


function deleteItemFromTable(tab, item)
	for key, value in pairs(tab) do
	    if value == item then
	        table.remove( tab, key )
	        return true
	    end
	end

	return false
end


Bookstore = {}
Bookstore_mt = { __index = Bookstore }

function Bookstore.create(self)
	local attributes = { books = {}, isbnMap = {} }
    setmetatable( attributes, Bookstore_mt )
    return attributes
end


function Bookstore.loadBooks(self, filename)
	--[[
     The load books provides a very basic example of how the Argot
     TypeInputStream is used.  
     
     A booklist type is red from the selected filename.
	]]
	
    local f = io.open(filename, 'rb')
    
    if f == nil then
        print('File ' .. filename .. ' could not be opened')
		return
	end

    -- Create a map of data types required for this file.
    local typeMap = self:getDataFileTypeMap()

    function reader(size)
        print('reading ' .. size .. ' bytes')
        return f:read(size)
	end
        
    -- Create a typed input stream using file and type map.
    local typeInputStream = argot.TypeInputStream( typeMap, reader )

    -- The file structure is defined by booklist.  Read the books.
    print('Reading booklist')
    typeInputStream:read_named_object( argot.TypeName("booklist") )
    print('reading booklist done')
    local books = argot.convert_to_lua_object(typeInputStream.stack.ptr)
    -- print('Books :', books)

    for idx, book in ipairs(books) do
		self:addBook( book )
 	end
end


function Bookstore.saveBooks(self, filename)
	--[[
     The save books method is the exact reverse of the read books.
     An array of books is passed to Argot to be written to file.
	]]
	
    local f = io.open(filename, 'wb')

	function outputStream(buffer)
        print('writing ' .. string.len(buffer) .. ' bytes')
        f:write(buffer)
        f:flush()
        return string.len(buffer)
	end
    
    local typeMap = self:getDataFileTypeMap()
    local typeOutputStream = argot.TypeOutputStream( typeMap, outputStream )
    typeOutputStream:write_named_object( argot.TypeName("booklist"), self:getAllBooks() )
end

function Bookstore.getDataFileTypeMap(self)
	--[[
     The TypeMap is a central concept to Argot.  It maps selected
     data types from the internal system to the external system.
     In this case only a small number of data types is used in the 
     external data file used to store the book list. 
	]]
	
    local library = argot.TypeLibrarySingleton_getDefault()
    local map = argot.TypeMap( library )
    map:map( 1, library:get_id( argot.TypeName("book") ) )
    map:map( 2, library:get_id( argot.TypeName("booklist") ))
    map:map( 3, library:get_id( argot.TypeName("u8ascii") ))
    map:map( 4, library:get_id( argot.TypeName("u8") ))
    return map
end


-- The rest of the bookstore implementation follows.
function Bookstore.addBook(self, book)
    local bookCheck = self.isbnMap[ book.ISBN ]
	if bookCheck ~= nil then return false end

    self.isbnMap[ book.ISBN ] = book
    table.insert(self.books, book)
    return true
end

function Bookstore.removeBook( self, isbn )
    local book = self:getBookByIsbn(isbn)
    deleteItemFromTable(self.books, book)
    self.isbnMap[ isbn ] = nil
end

function Bookstore.findBooksByAuthor( self, name )
	local result = {}
	for key, book in ipairs(self.books) do
		if book.author == name then
    		table.insert(result, book)
		end
    end
    return result
end

function Bookstore.getBookByIsbn( self, isbn )
    local book = self.isbnMap[ isbn ]
    -- except KeyError:
    --    raise BookNotFoundException( isbn )

    return book
end

function Bookstore.getAllBooks( self )
    return self.books
end
